#ifndef     __THREAD_H
#define     __THREAD_H

#include "../rtx/rtx_os.h"
#include "../utils/nocopyable.h"

namespace rtos {

template <typename Executor, uint32_t StackSize, osPriority_t Priority, uint32_t Attr = osThreadDetached>
class Thread : private utils::NoCopyable {
public:
    void start(const char *name = "other") {
        osThreadAttr_t attr;
		attr.name = name;
        attr.attr_bits = Attr;
        attr.cb_mem = m_cb_mem;
        attr.cb_size = osRtxThreadCbSize;
        attr.stack_mem = m_stack_mem;
        attr.stack_size = StackSize;
        attr.priority = Priority;
        attr.tz_module = 0;
        attr.reserved = 0;
        m_id = osThreadNew(exec_main, this, &attr);
    }

protected:
    inline static osStatus_t yield() {
        return osThreadYield();
    }
     
    inline uint32_t set_flags(uint32_t flags) {
        return osThreadFlagsSet(m_id, flags);
    }

    static inline uint32_t clr_flags(uint32_t flags) {
        return osThreadFlagsClear(flags);
    }

    static inline uint32_t wait_all_flags(uint32_t flags, uint32_t timeout = osWaitForever) {
        return osThreadFlagsWait(flags, osFlagsWaitAll, timeout);
    }

    static inline uint32_t wait_any_flags(uint32_t flags, uint32_t timeout = osWaitForever) {
        return osThreadFlagsWait(flags, osFlagsWaitAny, timeout);
    }

public:
    inline osStatus_t join() {
        osThreadJoin(m_id);
    }

private:
    static void exec_main(void *args) {
        static_cast<Executor *>(args)->run();
        osThreadExit();
    }

private:
    osThreadId_t m_id;
    uint64_t m_cb_mem[ (osRtxThreadCbSize + 7) / 8 ];
    uint64_t m_stack_mem[ (StackSize + 7) / 8 ];
};

}

#endif
